﻿using AdvancedPoll.Models;
using AdvancedPoll.Services;
using AdvancedPoll.ViewModels;
using Orchard.ContentManagement;
using Orchard.Core.Contents.Controllers;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Settings;
using Orchard.UI.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Web;
using System.Web.Mvc;
using System.Xml;
using System.Xml.Serialization;

namespace AdvancedPoll.Controllers
{
    [ComVisible(false)]
    public class AdminController : Controller
    {
        private readonly IPollService _pollService;
        private readonly ISiteService _siteService;
        private readonly IXmlService _xmlService;

        public AdminController(
            IPollService pollService,
            ISiteService siteService,
            IXmlService xmlService,
            IShapeFactory shapeFactory)
        {
            _pollService = pollService;
            _siteService = siteService;
            Shape = shapeFactory;
            _xmlService = xmlService;
            T = NullLocalizer.Instance;
        }

        dynamic Shape { get; set; }

        public Localizer T { get; set; }

        public ActionResult Index(PollIndexOptions options, PagerParameters pagerParameters)
        {
            var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);

            // Default options
            if (options == null)
                options = new PollIndexOptions();

            // Filtering
            IContentQuery<PollPart, PollRecord> pollsQuery;
            switch (options.Filter)
            {
                case PollIndexFilter.All:
                    pollsQuery = _pollService.GetPolls();
                    break;
                case PollIndexFilter.Open:
                    pollsQuery = _pollService.GetPolls(true);
                    break;
                case PollIndexFilter.Closed:
                    pollsQuery = _pollService.GetPolls(false);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("options");
            }

            var pagerShape = Shape.Pager(pager).TotalItemCount(pollsQuery.Count());
            var entries = pollsQuery
                .OrderByDescending<PollRecord>(poll => poll.Id)
                .Slice(pager.GetStartIndex(), pager.PageSize)
                .ToList()
                .Select(poll => CreatePollEntry(poll.Record));

            var model = new AdminIndexViewModel
            {
                Polls = entries.ToList(),
                Options = options,
                Pager = pagerShape
            };
            return View((object)model);
        }

        public ActionResult Edit(int id)
        {
            var poll =_pollService.GetPoll(id);
            var model = new PollViewModel
            {
                Question = poll.Question,
                Open = poll.OpenDateUtc.Value,
                Close = poll.CloseDateUtc.Value,
                MaxVotes = poll.MaxVotes,
                Shown = poll.Shown,
                Choices = _pollService.GetChoices(id)
                                .Select(c => new ChoiceEntry { Choice = c, Action = "Alter" })
                                .ToList()
            };
            return View((object)model);
        }

        [HttpPost]
        [FormValueRequired("submit.BulkEdit")]
        public ActionResult Index(FormCollection form)
        {
            var viewModel = new AdminIndexViewModel { Polls = new List<PollEntry>(), Options = new PollIndexOptions() };
            UpdateModel(viewModel);

            IEnumerable<PollEntry> checkedEntries = viewModel.Polls.Where(p => p.IsChecked);
            switch (viewModel.Options.BulkAction)
            {
                case PollIndexBulkAction.None:
                    break;
                case PollIndexBulkAction.Open:
                    foreach (PollEntry entry in checkedEntries)
                    {
                        _pollService.OpenPoll(entry.Poll.Id);
                    }
                    break;
                case PollIndexBulkAction.Close:
                    foreach (PollEntry entry in checkedEntries)
                    {
                        _pollService.ClosePoll(entry.Poll.Id);
                    }
                    break;
                case PollIndexBulkAction.Delete:
                    foreach (PollEntry entry in checkedEntries)
                    {
                        _pollService.DeletePoll(entry.Poll.Id);
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException("form");
            }
            return RedirectToAction("Index");
        }

        public ActionResult Details(int id)
        {
            var poll = _pollService.GetPoll(id);
            var choices = _pollService.GetChoices(id);
            var results = _pollService.GetPollWithResult(id);
            var votes = _pollService.GetVotes(id);
            var minDate = votes.Count() == 0 ? DateTime.Now : votes.Min(v => v.VoteDateUtc);
            var maxDate = votes.Count() == 0 ? DateTime.Now : votes.Max(v => v.VoteDateUtc);
            var result1 = new List<VoteEntry>();
            var result2 = new List<VoteEntry>();
            var result3 = new List<VoteEntry>();

            foreach (var choice in choices)
            {
                var entry1 = new VoteEntry
                {
                    Choice = choice,
                    Results = new Dictionary<DateTime, int>()
                };
                var entry2 = new VoteEntry
                {
                    Choice = choice,
                    IsAnonymous = true,
                    Results = new Dictionary<DateTime, int>()
                };
                var entry3 = new VoteEntry
                {
                    Choice = choice,
                    IsAnonymous = false,
                    Results = new Dictionary<DateTime, int>()
                };
                for (var date = minDate.Date; date <= maxDate.Date; date = date.AddDays(1))
                {
                    entry1.Results.Add(new DateTime(date.Ticks), votes.Count(v => v.PollChoiceRecord.Id == choice.Id && v.VoteDateUtc.Date == date));
                    entry2.Results.Add(new DateTime(date.Ticks), votes.Count(v => v.PollChoiceRecord.Id == choice.Id && v.VoteDateUtc.Date == date && v.Username == "Anonymous"));
                    entry3.Results.Add(new DateTime(date.Ticks), votes.Count(v => v.PollChoiceRecord.Id == choice.Id && v.VoteDateUtc.Date == date && v.Username != "Anonymous"));
                }
                result1.Add(entry1);
                result2.Add(entry2);
                result3.Add(entry3);
            }

            var model = new AdminDetailsViewModel
            {
                Question = poll.Question,
                Open = poll.OpenDateUtc.Value,
                Close = poll.CloseDateUtc.Value,
                MaxVotes = poll.MaxVotes,
                ResultByChoices = results.Choices,
                VotesByChoices = result1,
                AnonymVotesByChoices = result2,
                UserVotesByChoices = result3
            };
            return View(model);
        }

        [HttpPost]
        public ActionResult Delete(int id, string returnUrl)
        {
            var poll = _pollService.GetPoll(id);
            if (poll == null)
                return new HttpNotFoundResult();

            _pollService.DeletePoll(id);
            return Redirect(returnUrl);
        }

        [HttpPost]
        public ActionResult Open(int id, string returnUrl)
        {
            var poll = _pollService.GetPoll(id);
            if (poll == null)
                return new HttpNotFoundResult();

            _pollService.OpenPoll(id);
            return Redirect(returnUrl);
        }

        [HttpPost]
        public ActionResult Close(int id, string returnUrl)
        {
            var poll = _pollService.GetPoll(id);
            if (poll == null)
                return new HttpNotFoundResult();

            _pollService.ClosePoll(id);
            return Redirect(returnUrl);
        }

        public ActionResult Create()
        {
            return View();
        }

        public PollEntry CreatePollEntry(PollRecord poll)
        {
            if (poll == null)
                throw new ArgumentNullException("poll");
            return new PollEntry
            {
                Poll = poll,
                NumberOfChoices = _pollService.GetChoices(poll.Id).Count(),
                IsChecked = false
            };
        }

        
        [AllowAnonymous]
        [HttpPost]
        public ActionResult Vote(int id, string[] votes)
        {
            return this.Json(new { id = id, votes = votes });
        }

        [HttpGet]
        public FileResult ExportToXml(int id)
        {
            return File(_xmlService.ExportToXml(id), "text/xml", String.Format("Poll_{0}.xml", id));
        }

        [HttpPost]
        public ActionResult ImportFromXml()
        {
            for (int i = 0; i < Request.Files.Count; i++)
            {
                if ( Request.Files[i].InputStream!= null)
                    _xmlService.Import(Request.Files[i].InputStream);
            }
            return RedirectToAction("Index");
        }
    }
}